from odoo import fields, models, api, _
from ast import literal_eval
from odoo.osv.expression import AND

class SaleOrder(models.Model):
    _inherit = 'sale.order'

    meterial_id = fields.Many2one('order.meterial', string='物料汇总')
    order_meterial_count = fields.Integer(string='物料汇总数量', compute='_compute_order_meterial_count')
    wizard_id = fields.Integer(string='审批向导ID')

    def action_view_order_meterial(self):
        self.ensure_one()
        return {
            'type': 'ir.actions.act_window',
            'name': _('物料汇总'),
            'view_mode': 'form',
            'res_model': 'order.meterial',
            'target': 'current',
            'res_id': self.meterial_id.id
        }

    @api.depends('meterial_id')
    def _compute_order_meterial_count(self):
        for rec in self:
            if rec.meterial_id:
                rec.order_meterial_count = 1
            else:
                rec.order_meterial_count = 0

    def get_rule_lines(self):
        rule_lines = []
        for line in self.order_line:
            line_result = self.env['product.template'].recursive_bom_calculation(line.product_id, line.product_uom_qty)
            for product, product_info in line_result.items():
                checked = True
                rules = self.env['product.replace.rule']._get_match_rules(line, product)
                for rule in rules:
                    replace_products = rule._get_replace_products()
                    if replace_products:
                        replace_products_withself = replace_products.filtered(lambda x: x.id != product.id)
                        no_purchase_products = replace_products_withself.filtered(lambda x: not x.purchase_ok)
                        purchase_products = replace_products_withself.filtered(lambda x: x.purchase_ok)
                        if rule.check_quant:
                            products = self.env['stock.quant'].sudo().search([
                                ('location_id.usage', '=', 'internal'),
                                ('product_id', 'in', purchase_products.ids),
                                ('quantity', '>=', product_info.get('qty') * rule.convertible_ratio),
                                '|',
                                '|',
                                ('location_id.barcode', '=', 'WH-STOCK'),
                                ('location_id.location_id.barcode', '=', 'WH-STOCK'),
                                ('location_id.location_id.location_id.barcode', '=', 'WH-STOCK')
                            ]).product_id
                            replace_products_withself = products
                        replace_products_withself |= no_purchase_products
                        if rule.check_attributes:
                            for replace_product in replace_products_withself:
                                error_list = []
                                disable = False
                                for product_attribute_line in rule.product_attribute_lines:
                                    product_attribute = product_attribute_line.product_attributes
                                    attribute_val = product.product_template_coding_attribute_ids.filtered(
                                        lambda x: x.sb_material_coding_attribute_id == product_attribute).input_value
                                    target_attribute_val = replace_product.product_template_coding_attribute_ids.filtered(
                                        lambda x: x.sb_material_coding_attribute_id == product_attribute).input_value
                                    if product_attribute_line.domain:
                                        domain = AND([literal_eval(product_attribute_line.domain), [('id', '=', replace_product.product_tmpl_id.id)]])
                                        is_effective = self.env['product.template'].sudo().search(domain)
                                    else:
                                        is_effective = True
                                    if is_effective:
                                        if not replace_product.purchase_ok and '已停购' not in error_list:
                                            error_list.append(f'已停购')
                                        try:
                                            error_rate = (abs(float(target_attribute_val) - float(attribute_val)) / float(attribute_val)) * 100 if target_attribute_val and attribute_val else 0
                                        except:
                                            error_rate = 0
                                        if not error_rate:
                                            disable = True
                                        elif error_rate > product_attribute_line.allow_range:
                                            if product_attribute_line.type == 'disable':
                                                disable = True
                                                break
                                            else:
                                                error_list.append(f'{product_attribute.name}: {round(error_rate, 2)}%')
                                if not disable:
                                    target_total_price = product_info.get('qty') * rule.convertible_ratio * replace_product.standard_price
                                    diff_price = replace_product.standard_price - product.standard_price
                                    rule_lines.append((0, 0, {
                                        'product_id': product.id,
                                        'rule_id': rule.id,
                                        'target_product_id': replace_product.id,
                                        'qty': product_info.get('qty') * rule.convertible_ratio,
                                        'total_price': target_total_price,
                                        'diff_price': diff_price,
                                        'on_hand': self.env['stock.quant'].sudo().search([
                                            ('location_id.usage', '=', 'internal'),
                                            ('product_id', '=', replace_product.id),
                                            '|',
                                            '|',
                                            ('location_id.barcode', '=', 'WH-STOCK'),
                                            ('location_id.location_id.barcode', '=', 'WH-STOCK'),
                                            ('location_id.location_id.location_id.barcode', '=', 'WH-STOCK')
                                                    ], limit=1).quantity,
                                        'checked': checked,
                                        'error_info': '\n'.join(error_list),
                                        'optional_materials': replace_products_withself
                                    }))
                                    checked = False
        return rule_lines

    def _action_confirm(self):
        if not self.meterial_id:
            meterial_id = self.create_order_meterial()
            if meterial_id:
                self.meterial_id = meterial_id
        return super(SaleOrder, self)._action_confirm()

    def create_order_meterial(self):
        bom_dict = self.get_order_bom()
        meterial_line_ids = []
        for product, product_info in bom_dict.items():
            meterial_line_val = {
                'product_id': product.id,
                'product_qty': product_info.get('qty'),
                'real_product': product_info.get('real_product_id', False),
                'real_product_qty': product_info.get('real_product_qty', 0),
                'optional_materials': product_info.get('optional_materials', False),
                'convertible_ratio': product_info.get('convertible_ratio', 1)
            }
            meterial_line_ids.append((0, 0, meterial_line_val))
        if meterial_line_ids:
            meterial_id = self.env['order.meterial'].create({
                'line_ids': meterial_line_ids
            })
            return meterial_id
        return False

    def get_order_bom(self):
        result = {}
        for line in self.order_line:
            line_result = self.env['product.template'].recursive_bom_calculation(line.product_id, line.product_uom_qty)
            for product, product_info in line_result.items():
                wizard_id = self.env['workflow.check.wizard'].sudo().browse(self.wizard_id)
                rule_line = wizard_id.rule_lines.filtered(lambda x: x.product_id == product and x.target_product_id and x.checked)
                if rule_line:
                    line_result.update({
                        product: {
                            'qty': product_info.get('qty'),
                            'real_product_id': rule_line.target_product_id.id,
                            'real_product_qty': rule_line.qty,
                            'convertible_ratio': rule_line.rule_id.convertible_ratio,
                            'rule_domain': rule_line.rule_id.rule_domain,
                            'product_domain': rule_line.rule_id.product_domain,
                            'optional_materials': rule_line.optional_materials
                        }
                    })
            result.update(line_result)
        return result

